﻿using System;
using System.Threading;

namespace FscNET.Apm
{
    public static class InterlockedEx
    {
        // Methods
        //public static unsafe uint Add(ref uint location, int value)
        //{
        //    fixed (uint* numRef = ((uint*) location))
        //    {
        //        int* numPtr = (int*) numRef;
        //        return (uint) Interlocked.Add(ref (int) ref numPtr, value);
        //    }
        //}

        public static int AddModulo(ref int target, int value, int modulo)
        {
            int num;
            int num2 = target;
            do
            {
                num = num2;
                num2 = Interlocked.CompareExchange(ref target, (num + value) % modulo, num);
            }
            while (num != num2);
            return num2;
        }

        public static int And(ref int target, int with)
        {
            int num;
            int num2 = target;
            do
            {
                num = num2;
                num2 = Interlocked.CompareExchange(ref target, num & with, num);
            }
            while (num != num2);
            return num2;
        }

        public static bool BitTestAndCompliment(ref int target, int bitNumber)
        {
            int with = ((int)1) << bitNumber;
            return ((Xor(ref target, with) & with) != 0);
        }

        public static bool BitTestAndReset(ref int target, int bitNumber)
        {
            int num = ((int)1) << bitNumber;
            return ((And(ref target, ~num) & num) != 0);
        }

        public static bool BitTestAndSet(ref int target, int bitNumber)
        {
            int with = ((int)1) << bitNumber;
            return ((Or(ref target, with) & with) != 0);
        }

        /// <summary>Compares two 32-bit unsigned integers for equality and, if they are equal, replaces one of the values.</summary>
        /// <remarks>If comparand and the value in location are equal, then value is stored in location. Otherwise, no operation is performed. The compare and exchange operations are performed as an atomic operation. The return value of CompareExchange is the original value in location, whether or not the exchange takes place.</remarks>
        /// <param name="location">The destination, whose value is compared with comparand and possibly replaced.</param>
        /// <param name="value">The value that replaces the destination value if the comparison results in equality.</param>
        /// <param name="comparand">The value that is compared to the value at <paramref name="location"/>.</param>
        /// <returns>The original value in location.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "0#")]
        public static UInt32 CompareExchange(ref UInt32 location, UInt32 value, UInt32 comparand)
        {
            unsafe
            {
                fixed (UInt32* p = &location)
                {
                    Int32* p2 = (Int32*)p;
                    return (UInt32)Interlocked.CompareExchange(ref *p2, (Int32)value, (Int32)comparand);
                }
            }
        }

        //public static uint Decrement(ref uint location)
        //{
        //    return Add(ref location, -1);
        //}

        public static int DecrementIfGreaterThan(ref int target, int lowValue)
        {
            int num;
            int num2 = target;
            do
            {
                num = num2;
                num2 = Interlocked.CompareExchange(ref target, (num > lowValue) ? (num - 1) : num, num);
            }
            while (num != num2);
            return num2;
        }

        /// <summary>Sets a 32-bit unsigned integer to a specified value and returns the original value, as an atomic operation.</summary>
        /// <param name="location">The variable to set to the specified value.</param>
        /// <param name="value">The value to which the <paramref name="location"/> parameter is set.</param>
        /// <returns>The original value of <paramref name="location"/>.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "0#")]
        public static UInt32 Exchange(ref UInt32 location, UInt32 value)
        {
            unsafe
            {
                fixed (UInt32* p = &location)
                {
                    Int32* p2 = (Int32*)p;
                    return (UInt32)Interlocked.Exchange(ref *p2, (Int32)value);
                }
            }
        }

        public static bool IfThen(ref int value, int @if, int then)
        {
            return (Interlocked.CompareExchange(ref value, then, @if) == @if);
        }

        public static bool IfThen<T>(ref T value, T @if, T then) where T : class
        {
            return (Interlocked.CompareExchange<T>(ref value, then, @if) == @if);
        }

        //public static bool IfThen(ref uint value, uint @if, uint then)
        //{
        //    return (CompareExchange(ref value, then, @if) == @if);
        //}

        public static bool IfThen<T>(ref T value, T @if, T then, out T previousValue) where T : class
        {
            previousValue = Interlocked.CompareExchange<T>(ref value, then, @if);
            return (((T)previousValue) == @if);
        }

        public static bool IfThen(ref int value, int @if, int then, out int previousValue)
        {
            previousValue = Interlocked.CompareExchange(ref value, then, @if);
            return (previousValue == @if);
        }

        //public static bool IfThen(ref uint value, uint @if, uint then, out uint previousValue)
        //{
        //    previousValue = CompareExchange(ref value, then, @if);
        //    return (previousValue == @if);
        //}

        //public static uint Increment(ref uint location)
        //{
        //    return Add(ref location, 1);
        //}

        public static int MaskedAdd(ref int target, int value, int mask)
        {
            int num;
            int num2 = target;
            do
            {
                num = num2 & mask;
                num2 = Interlocked.CompareExchange(ref target, num + value, num);
            }
            while (num != num2);
            return num2;
        }

        public static int MaskedAnd(ref int target, int with, int mask)
        {
            int num;
            int num2 = target;
            do
            {
                num = num2 & mask;
                num2 = Interlocked.CompareExchange(ref target, num & with, num);
            }
            while (num != num2);
            return num2;
        }

        public static int MaskedExchange(ref int target, int mask, int value)
        {
            int num;
            int comparand = target;
            do
            {
                num = comparand;
                comparand = Interlocked.CompareExchange(ref target, (num & ~mask) | value, comparand);
            }
            while (num != comparand);
            return comparand;
        }

        public static int MaskedOr(ref int target, int with, int mask)
        {
            int num;
            int num2 = target;
            do
            {
                num = num2 & mask;
                num2 = Interlocked.CompareExchange(ref target, num | with, num);
            }
            while (num != num2);
            return num2;
        }

        public static int MaskedXor(ref int target, int with, int mask)
        {
            int num;
            int num2 = target;
            do
            {
                num = num2 & mask;
                num2 = Interlocked.CompareExchange(ref target, num ^ with, num);
            }
            while (num != num2);
            return num2;
        }

        public static int Max(ref int target, int value)
        {
            int num;
            int num2 = target;
            do
            {
                num = num2;
                num2 = Interlocked.CompareExchange(ref target, Math.Max(num, value), num);
            }
            while (num != num2);
            return num2;
        }

        public static long Max(ref long target, long value)
        {
            long num;
            long num2 = target;
            do
            {
                num = num2;
                num2 = Interlocked.CompareExchange(ref target, Math.Max(num, value), num);
            }
            while (num != num2);
            return num2;
        }

        public static int Min(ref int target, int value)
        {
            int num;
            int num2 = target;
            do
            {
                num = num2;
                num2 = Interlocked.CompareExchange(ref target, Math.Min(num, value), num);
            }
            while (num != num2);
            return num2;
        }

        public static long Min(ref long target, long value)
        {
            long num;
            long num2 = target;
            do
            {
                num = num2;
                num2 = Interlocked.CompareExchange(ref target, Math.Min(num, value), num);
            }
            while (num != num2);
            return num2;
        }

        public static TResult Morph<TResult, TArgument>(ref int target, TArgument argument, Morpher<TResult, TArgument> morpher)
        {
            TResult local;
            int num;
            int num2 = target;
            do
            {
                num = num2;
                num2 = Interlocked.CompareExchange(ref target, morpher(num, argument, out local), num);
            }
            while (num != num2);
            return local;
        }

        public static int Or(ref int target, int with)
        {
            int num;
            int num2 = target;
            do
            {
                num = num2;
                num2 = Interlocked.CompareExchange(ref target, num | with, num);
            }
            while (num != num2);
            return num2;
        }

        public static long Or(ref long target, long with)
        {
            long num;
            long num2 = target;
            do
            {
                num = num2;
                num2 = Interlocked.CompareExchange(ref target, num | with, num);
            }
            while (num != num2);
            return num2;
        }

        public static int Xor(ref int target, int with)
        {
            int num;
            int num2 = target;
            do
            {
                num = num2;
                num2 = Interlocked.CompareExchange(ref target, num ^ with, num);
            }
            while (num != num2);
            return num2;
        }

        // Nested Types
        public delegate int Morpher<TResult, TArgument>(int startValue, TArgument argument, out TResult morphResult);
    }

}
